/**
 * Unix socket 通过本地创建一个sock文件的形式，让多个客户端连接实现本地机器多个进程之间的
 * 通信。
 * 用法和普通IP协议的Socket用法是差不多的。
 */
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>

#include <iostream>
#include <thread>

/* 下面几个头文件用于实现日志函数 */

#include <fstream>
#include <iomanip>
#include <ctime>
#include <chrono>
#include <mutex>

std::mutex fileLogMutex;
std::string fileLogName;

void fileLog(std::string log) {
    using std::chrono::system_clock;
    using std::chrono::milliseconds;
    using std::chrono::duration_cast;
    fileLogMutex.lock();
    std::ofstream file(fileLogName, std::ofstream::app);
    if (file.is_open()) {
        auto time_point_now = system_clock::now();
        auto now_sec = system_clock::to_time_t(time_point_now); // now_sec只能精确到秒
        auto time_point_sec = system_clock::from_time_t(now_sec);
        auto diff_time = time_point_now - time_point_sec; // diff_time>0, 精确的时间和now_sec之间的差距，不足1秒的部分
        file << std::put_time(std::localtime(&now_sec), "%FT%T.");
        file << std::setw(3) << std::setfill('0') << duration_cast<milliseconds>(diff_time).count();
        file << std::put_time(std::localtime(&now_sec), "%z");
        file << " [unix_socket_server] " << log;
        file << std::endl;
    }
    fileLogMutex.unlock();
}

int main() {
    using namespace std;
    struct sockaddr_un socketAddress;
    string socketFile("/tmp/test_unix_socket_3271846.sock"); // 这里路径长度不能大于107字节

    fileLogName = socketFile + ".log"; // 日志文件文件名

    auto socketId = socket(AF_UNIX, SOCK_STREAM, 0);
    if (-1 == socketId) {
        cout << "Call socket fail, function return " << socketId << endl;
        return 1;
    }

    socketAddress.sun_family = AF_UNIX;
    socketAddress.sun_path[socketFile.length()] = '\0';
    socketFile.copy(socketAddress.sun_path, socketFile.length());

    remove(socketFile.c_str()); // 尝试删除文件先，否则bing失败。这个操作适用于Ctrl+C结束，这种场景会遗留一个socketFile没有删除

    if (0 != bind(socketId, (sockaddr *)&socketAddress, sizeof(socketAddress))) {
        cout << "Call bind fail" << endl;
        return 1;
    }

    if (0 != listen(socketId, SOMAXCONN)) {
        cout << "Call listen fail" << endl;
        return 1;
    }

    cout << "Waiting for client connection" << endl;
    while (true) { // 接受请求阶段
        auto clientConnectSocket = accept(socketId, NULL, NULL); // clientConnectSocket 由子线程close
        if (-1 == clientConnectSocket) {
            cout << "Call accept, return -1" << endl;
            return 1;
        }

        cout << "A client initiated connection was received" << endl;
        // 创建新的线程并分离线程，处理请求
        thread([](int clientConnectSocket) {
            char buffer[255];
            auto recvDataLength = recv(clientConnectSocket, buffer, 254, 0);
            if (recvDataLength <= 0) {
                fileLog(string("Error, received data with length of 0 bytes"));
                return;
            }

            buffer[recvDataLength] = '\0';

            fileLog(string(buffer)); // 日志记录接受到的内容

            string responseMessage("Hello, I am unix_socket_server.");
            auto sendDataLength = send(clientConnectSocket, responseMessage.c_str(), responseMessage.length(), 0);
            if (responseMessage.length() != sendDataLength) {
                fileLog(string("Error, data not sent completely"));
            }

            close(clientConnectSocket);
        }, clientConnectSocket).detach();
    }

    return 0;
}

